﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;

namespace GFluentCode.Tools
{
    /// <summary>
    /// 串口类
    /// </summary>
    public class FluentSerialPort : INotifyPropertyChanged,IDisposable
    {
        #region 常用设备项，为外部展示提供常用数据

        /// <summary>
        /// 常用到的波特率值
        /// </summary>
        public static int[] BaudRateArr = new int[]
        {
            110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 256000
        };
        /// <summary>
        /// 常用的数据位
        /// </summary>
        public static byte[] DataBitsArr = new byte[] { 5, 6, 7, 8 };
        /// <summary>
        /// 常用停止位
        /// </summary>
        public static string[] StopBitArr = new string[] { "不使用", "1", "1.5", "2" };
        /// <summary>
        /// 奇偶校验位
        /// </summary>
        public static string[] ParityArr = new string[] { "不发生奇偶校验检查", "奇数", "偶数", "保留为1", "保留为0" };
        #endregion

        #region 数据接收完成事件
        /// <summary>
        /// 事件委托
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="data"></param>
        public delegate void DataReceivedHandler(FluentSerialPort sender, byte[] data);
        /// <summary>
        /// 数据接收事件
        /// </summary>
        public event DataReceivedHandler OnDataReceived;
        #endregion

        //public delegate bool AnalysisData(string head, string end, bool isByts, out byte[] ProtocolBytes);

        //public event AnalysisData OnAnalysisData;

        /// <summary>
        /// 属性变化 
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        #region 属性

        /// <summary>
        /// 微软提供的串口类对象
        /// </summary>
        public SerialPort ComObj { get; private set; }

        /// <summary>
        /// 停止位对应枚举参数
        /// </summary>
        private static readonly Dictionary<string, StopBits> DicStopBits = new Dictionary<string, StopBits>()
        {
            {"不使用", StopBits.None},
            {"1", StopBits.One},
            {"2", StopBits.Two},
            {"1.5", StopBits.OnePointFive}
        };
        /// <summary>
        /// 奇偶校验位对应枚举参数
        /// </summary>
        private static readonly Dictionary<string, Parity> DicParitys = new Dictionary<string, Parity>()
        {
            {"不发生奇偶校验检查", Parity.None},
            {"奇数", Parity.Odd},
            {"偶数", Parity.Even},
            {"保留为1", Parity.Mark},
            {"保留为0", Parity.Space}
        };

        private Queue<byte> _recDataQueue = new Queue<byte>();

        /// <summary>
        /// 串口名
        /// </summary>
        public string PortName { get; set; }
        /// <summary>
        /// 波特率
        /// </summary>
        public int BaudRate { get; set; }
        /// <summary>
        /// 数据位
        /// </summary>
        public byte DataBits { get; set; }
        /// <summary>
        /// 停止位
        /// </summary>
        public string StopBit { get; set; }
        /// <summary>
        /// 奇偶校验位项
        /// </summary>
        public string ParityItem { get; set; }
        /// <summary>
        /// 接收数据时等待的时间间隔,单位为毫秒
        /// </summary>
        public int ReceiveSleepTimeLen { get; set; }
        /// <summary>
        /// 使用缓存模式对接收数据进行缓存
        /// </summary>
        public bool UseBufferMode { get; set; }
        public int BufferSize { get; set; }
        private string _msg;
        /// <summary>
        /// 内容消息
        /// </summary>
        public string Msg
        {
            get { return _msg; }
            set
            {
                _msg = value;
                if (PropertyChanged != null)
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Msg"));
            }
        }

        /// <summary>
        /// 首次连接时间
        /// </summary>
        private DateTime? _firstTime;
        public DateTime? FirstTime
        {
            get { return _firstTime; }
            set
            {
                _firstTime = value;
                if (PropertyChanged != null)
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("FirstTime"));
            }
        }
        /// <summary>
        /// 最后一次通信时间
        /// </summary>
        private DateTime? _lastTime;
        public DateTime? LastTime
        {
            get { return _lastTime; }
            set
            {
                _lastTime = value;
                if (PropertyChanged != null)
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("LastTime"));
            }
        }
        /// <summary>
        /// 最后一次通信数据
        /// </summary>
        public byte[] LastData { get; set; }
        /// <summary>
        /// Com数据解析类型 16进制 ASCII码
        /// </summary>
        public EnSerialPortDataType SerialPortDataType { get; set; }
        /// <summary>
        /// 其它Encoding,仅当ComDataType为other时有用
        /// </summary>
        public Encoding OtherEncoding { get; set; }
        /// <summary>
        /// 最后一次通信数据展示串
        /// </summary>
        private string _lastDataStr;
        public string LastDataStr
        {
            get
            {
                if (LastData == null || LastData.Length <= 0)
                    _lastDataStr = string.Empty;
                else
                {
                    switch (SerialPortDataType)
                    {
                        case EnSerialPortDataType.Hex:
                            _lastDataStr =
                                LastData.Aggregate(string.Empty, (a, b) => a += b.ToString("X2") + " ");
                            break;
                        case EnSerialPortDataType.ASCII:
                            _lastDataStr = Encoding.ASCII.GetString(LastData);
                            break;
                        case EnSerialPortDataType.UTF8:
                            _lastDataStr = Encoding.UTF8.GetString(LastData);
                            break;
                        case EnSerialPortDataType.Gb2312:
                            _lastDataStr = Encoding.GetEncoding("gb2312").GetString(LastData);
                            break;
                        case EnSerialPortDataType.Other:
                            if (OtherEncoding != null)
                                _lastDataStr = OtherEncoding.GetString(LastData);
                            else
                                _lastDataStr = string.Join(" ",
                                    LastData.Cast<byte>().Select(t => Convert.ToInt32(t).ToString("X2")));
                            break;
                        default:
                            _lastDataStr = string.Join(" ",
                                LastData.Cast<byte>().Select(t => Convert.ToInt32(t).ToString("X2")));
                            break;
                    }
                }
                return _lastDataStr;
            }
            set
            {
                _lastDataStr = value;
                if (PropertyChanged != null)
                    PropertyChanged.Invoke(this, new PropertyChangedEventArgs("LastDataStr"));
            }
        }

        private bool _isInReceived;

        /// <summary>
        /// 扩展属性，由使用者自行定义
        /// </summary>
        public object ExtendObj { get; set; }
        #endregion

        #region 基本操作

        /// <summary>
        /// 
        /// </summary>
        public FluentSerialPort()
        {
            PortName = "com1";
            BaudRate = 9600;
            DataBits = 8;
            StopBit = "1";
            ParityItem = "不发生奇偶校验检查";
            ReceiveSleepTimeLen = 1;
            BufferSize = 4096;
            ComObj = new SerialPort();
            ComObj.DataReceived += PortReceivedData;
            SetConfig();
        }

        /// <summary>
        /// 设置串口参数
        /// </summary>
        public void SetConfig()
        {
            ComObj.PortName = PortName;
            ComObj.BaudRate = BaudRate;
            ComObj.DataBits = DataBits;
            ComObj.Parity = Parity.None;
            //ComObj.ReadBufferSize = 1024;
            var sb = DicStopBits.Keys.Contains(StopBit)
                ? DicStopBits[StopBit]
                : StopBits.None;
            ComObj.StopBits = sb;

            var pi = DicParitys.Keys.Contains(ParityItem)
                ? DicParitys[ParityItem]
                : Parity.None;
            ComObj.Parity = pi;
            ComObj.ReadBufferSize = ComObj.WriteBufferSize = BufferSize;
        }

        /// <summary>
        /// 额外设置DisCardNull，DtrEnable，RtsEnable
        /// </summary>
        /// <param name="disCardNull">获取或设置一个值，该值指示 Null 字节在端口和接收缓冲区之间传输时是否被忽略。默认值为 false。</param>
        /// <param name="dtrEnable">获取或设置一个值，该值在串行通信过程中启用数据终端就绪 (DTR) 信号。默认值为 false。</param>
        /// <param name="rtsEnable">获取或设置一个值，该值指示在串行通信中是否启用请求发送 (RTS) 信号。默认值为 false。</param>
        public void SetConfig(bool disCardNull=false,bool dtrEnable=false,bool rtsEnable=false)
        {
            SetConfig();
            ComObj.DiscardNull = disCardNull;
            ComObj.DtrEnable = dtrEnable;
            ComObj.RtsEnable = rtsEnable;
        }
        /// <summary>
        /// 打开串口
        /// </summary>
        public bool Open()
        {
            try
            {
                if (ComObj.IsOpen)
                {
                    Msg = "Port Is Already Opened";
                    return false;
                }
                else
                {
                    ComObj.Open();
                    Msg = "Port Open Success";
                    FirstTime = DateTime.Now;
                    return true;
                }
            }
            catch (Exception ex)
            {
                Msg = "Port Open Error:" + ex.Message;
                return false;
            }
        }
        /// <summary>
        /// 关闭串口
        /// </summary>
        public void Close()
        {
            try
            {
                if (ComObj.IsOpen)
                {
                    while (_isInReceived)
                        SpinWait.SpinUntil(() => false, 1);
                    ComObj.Close();
                    Msg = "Port Close Success";
                }
                else
                    Msg = "Port Is Closed";
            }
            catch (Exception ex)
            {
                Msg = "Port Close Error:" + ex.Message;
            }
        }

        #endregion

        #region 接收数据

        /// <summary>
        /// 数据接收事件
        /// 默认将接收到的数据逐byte写入接收队列
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PortReceivedData(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                //_isInReceived = true;
                ////等待一段时长，让串口充分接收数据
                //if (ReceiveSleepTimeLen > 0)
                //    Thread.Sleep(ReceiveSleepTimeLen);

                //var sp = sender as SerialPort;
                //if (sp == null) return;
                //int count;
                //byte[] bs = null;
                ////接收为0时，还不断进入接收事件情况下，重启串口
                //if (sp.BytesToRead == 0)
                //{
                //    ComObj.Close();
                //    ComObj.Open();
                //    return;
                //}

                //count = sp.BytesToRead;
                //bs = new byte[count];
                //sp.Read(bs, 0, count);
                //if (UseBufferMode)
                //    bs.ToList().ForEach(Push);

                //LastData = bs;
                //LastTime = DateTime.Now;

                //OnDataReceived?.Invoke(this, bs);

                if (ComObj.BytesToRead != 0)
                {
                    lock (this)
                    {
                        _isInReceived = true;
                        if (ReceiveSleepTimeLen > 0)
                            Thread.Sleep(ReceiveSleepTimeLen);
                        else Thread.Sleep(50);

                        var arr = new byte[ComObj.ReadBufferSize];
                        var num = ComObj.Read(arr, 0, arr.Length);
                        if (num <= 0) return;
                        OnDataReceived?.Invoke(this, arr);
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                _isInReceived = false;
            }
        }

        #endregion

        #region 接收数据队列操作

        /// <summary>
        /// 从队列头读取一位接收数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Pop(out byte data)
        {
            lock (_recDataQueue)
            {
                data = 0;
                if (_recDataQueue.Count <= 0)
                    return false;
                data = _recDataQueue.Dequeue();
                return true;
            }
        }
        /// <summary>
        /// 添加一位接收数据到队列尾
        /// </summary>
        /// <param name="item"></param>
        private void Push(byte item)
        {
            lock (_recDataQueue)
            {
                _recDataQueue.Enqueue(item);
            }
        }

        /// <summary>
        /// 复制缓存队列中的数据
        /// </summary>
        /// <returns></returns>
        public byte[] CopyQueue()
        {
            lock (_recDataQueue)
            {
                var count = _recDataQueue.Count;
                var cp = new byte[count];
                _recDataQueue.CopyTo(cp, 0);
                return cp;
            }
        }

        #endregion

        #region 发送

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="sendData"></param>
        /// <returns></returns>
        public virtual bool Send(byte[] sendData)
        {
            try
            {
                if (!ComObj.IsOpen)
                {
                    Msg = "Port Is Closed";
                    return false;
                }
                ComObj.Write(sendData, 0, sendData.Length);
                return true;
            }
            catch (Exception ex)
            {
                Msg = "Port Error:" + ex.Message;
                return false;
            }
        }

        #endregion
        /// <summary>
        /// 
        /// </summary>
        ~FluentSerialPort()
        {
            this.Dispose();
        }
        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (ComObj == null) return;

            if (ComObj.IsOpen) ComObj.Close();
            ComObj.Dispose();
            ComObj = null;
        }
    }

    /// <summary>
    /// 串口数据字符类型
    /// </summary>
    public enum EnSerialPortDataType
    {
        Hex,
        ASCII,
        UTF8,
        Gb2312,
        Other
    }
}
